package com.codegen;

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CodeGenerator {

    private static final String BACKEND_BASE_PATH = "generated-code/backend/src/main/java";
    private static final String FRONTEND_BASE_PATH = "generated-code/frontend/src";
    private Configuration cfg;

    public CodeGenerator(String templateDir) throws Exception {
        // 初始化FreeMarker配置
        cfg = new Configuration(Configuration.VERSION_2_3_31);
        cfg.setDirectoryForTemplateLoading(new File(templateDir));
        cfg.setDefaultEncoding("UTF-8");
    }

    public void generate(String databaseName, String basePackage, String url, String userName, String password) throws Exception {
        // 1. 获取数据库元数据
        DatabaseMetaReader metaReader = new DatabaseMetaReader();
        List<TableMeta> tables = metaReader.readDatabaseMeta(databaseName, url, userName, password);

        System.out.println("成功读取数据库 '" + databaseName + "', 找到 " + tables.size() + " 张表");

        // 2. 创建基础包目录
        createPackageDirs(basePackage);

        // 3. 为每张表生成代码
        for (TableMeta table : tables) {
            System.out.println("正在生成表 [" + table.getName() + "] 代码...");
            generateBackendCode(table, basePackage);
            generateFrontendCode(table);
        }

        System.out.println("所有代码生成完成！请查看 generated-code 目录");
    }

    private void createPackageDirs(String basePackage) {
        // 创建后端包结构
        String packagePath = basePackage.replace('.', '/');
        String[] backendDirs = {
                "entity",
                "dto",
                "mapper",
                "service",
                "service/impl",
                "controller"
        };

        for (String dir : backendDirs) {
            new File(BACKEND_BASE_PATH + "/" + packagePath + "/" + dir).mkdirs();
        }

        // 创建前端目录
        new File(FRONTEND_BASE_PATH + "/api").mkdirs();
        new File(FRONTEND_BASE_PATH + "/views").mkdirs();

        // 创建Mapper XML目录
        new File(BACKEND_BASE_PATH + "/../../resources/mapper").mkdirs();
    }

    private void generateBackendCode(TableMeta table, String basePackage) throws Exception {
        // 准备数据模型
        Map<String, Object> data = new HashMap<>();
        String entityName = CodeGeneratorUtils.toCamelCase(table.getName(), true);
        String lowEntityName = CodeGeneratorUtils.toCamelCase(table.getName(), false);

        data.put("table", table);
        data.put("entityName", entityName);
        data.put("lowEntityName", lowEntityName);
        data.put("package", basePackage);
        data.put("hasDate", hasDateField(table));
        data.put("hasBigDecimal", hasBigDecimalField(table));

        // 注册工具方法到数据模型
//        data.put("convertType", (Function<String, String>) s -> CodeGeneratorUtils.convertType(s));
//        data.put("toCamelCase", (Function<String, String>) name -> CodeGeneratorUtils.toCamelCase(name, false));

        // 注册函数（关键修改！）
        data.put("CodeGeneratorUtils", new CodeGeneratorUtils());

        // 生成实体类
        generateFile("backend/entity.ftl",
                BACKEND_BASE_PATH + "/" + basePackage.replace('.', '/') + "/entity/" + entityName + ".java",
                data);

        // 生成Query DTO
        generateFile("backend/query.ftl",
                BACKEND_BASE_PATH + "/" + basePackage.replace('.', '/') + "/dto/" + entityName + "QueryDTO.java",
                data);

        // 生成Mapper接口
        generateFile("backend/mapper.ftl",
                BACKEND_BASE_PATH + "/" + basePackage.replace('.', '/') + "/mapper/" + entityName + "Mapper.java",
                data);

        // 生成Mapper XML文件
        generateFile("backend/mapperXml.ftl",
                BACKEND_BASE_PATH + "/../../resources/mapper/" + entityName + "Mapper.xml",
                data);

        // 生成Service接口
        generateFile("backend/service.ftl",
                BACKEND_BASE_PATH + "/" + basePackage.replace('.', '/') + "/service/" + entityName + "Service.java",
                data);

        // 生成ServiceImpl实现类
        generateFile("backend/serviceImpl.ftl",
                BACKEND_BASE_PATH + "/" + basePackage.replace('.', '/') + "/service/impl/" + entityName + "ServiceImpl.java",
                data);

        // 生成Controller类
        generateFile("backend/controller.ftl",
                BACKEND_BASE_PATH + "/" + basePackage.replace('.', '/') + "/controller/" + entityName + "Controller.java",
                data);
    }

    private void generateFrontendCode(TableMeta table) throws Exception {
        Map<String, Object> data = new HashMap<>();
        String entityName = CodeGeneratorUtils.toCamelCase(table.getName(), true);
        String frontEntityName = CodeGeneratorUtils.toCamelCase(table.getName(), false);

        data.put("table", table);
        data.put("entityName", entityName);
        data.put("lowEntityName", frontEntityName);
        data.put("frontEntityName", frontEntityName);


        // 注册函数（关键修改！）
        data.put("CodeGeneratorUtils", new CodeGeneratorUtils());

        // 创建视图目录
        String viewPath = FRONTEND_BASE_PATH + "/views/" + frontEntityName;
        new File(viewPath).mkdirs();

        // 生成API文件
        generateFile("frontend/api.ftl",
                FRONTEND_BASE_PATH + "/api/" + frontEntityName + ".js",
                data);

        // 生成列表页面
        generateFile("frontend/list.ftl",
                viewPath + "/List.vue",
                data);

        // 生成表单页面
        new File(viewPath + "/components").mkdirs();
        generateFile("frontend/form.ftl",
                viewPath + "/components/Form.vue",
                data);
    }

    private void generateFile(String templateName, String outputPath, Object data) throws Exception {
        Template template = cfg.getTemplate(templateName);
        File outputFile = new File(outputPath);

        // 创建父目录（如果需要）
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }

        try (Writer out = new FileWriter(outputFile)) {
            template.process(data, out);
        }
    }

    private boolean hasDateField(TableMeta table) {
        return table.getColumns().stream().anyMatch(c ->
                c.getType().equalsIgnoreCase("datetime") ||
                        c.getType().equalsIgnoreCase("date") ||
                        c.getType().equalsIgnoreCase("timestamp"));
    }

    private boolean hasBigDecimalField(TableMeta table) {
        return table.getColumns().stream().anyMatch(c ->
                c.getType().equalsIgnoreCase("decimal"));
    }

}